import { Asset, AssetManager, assetManager, dragonBones, Node, v2, Vec2 } from "cc";
import { Singleton } from "./Common";

const RES:string = 'resources';

type CompleteCall = (res:Asset|Asset[])=>void;
type ProgressCall = (finish:number,total:number,item:any)=>void;
export default class assetsMgr extends Singleton<assetsMgr>{
    private _bundleCache:Map<string,AssetManager.Bundle> = new Map<string,AssetManager.Bundle>();
    private _waitTimer:any = null;
    private _isWait:boolean = false;
    private _waitArray:string[] = [];

    constructor(){
        super();
    }

    protected onDestroy(){
    }

    protected onInit(){
        this._bundleCache.set('resources',assetManager.resources);
    }

    /**
     * 异步加载资源\目录
     * resources目录下
     * res/img/aa       加载资源 resources/res/aa
     * res/img/bb/      加载目录 resources/res/bb/
     * 加载子包
     * res/aa:bundle    加载子包bundle下res/aa的文件资源
     * res/bb/:bundle   加载子包bundle下res/bb文件夹下的所有资源
     * http://... https://... 加载一个远程资源
     * @param path 文件(夹)路径
     * @param onComplete  加载结束回调
     * @param type          资源类型
     * @param onProgress    加载进度
     */
    public load(path:string,onComplete:CompleteCall,type?: typeof Asset,onProgress?:ProgressCall){
        const tmpPath:string = path.toLowerCase();
        if (tmpPath.indexOf('http') === 0){
            this._loadRemote(path,onComplete);
        } else if (tmpPath.lastIndexOf('/') === tmpPath.length - 1){
            this._loadDir(path,onComplete,type,onProgress);
        } else {
            this._loadAsset(path,onComplete);
        }
    }

    /**
     * 同步(加载成功一个后才会加载下一个)加载一个数组资源
     * 注意不要文件和文件夹混杂，否则可能会出错
     * @param path 欲加载数组
     * @param onComplete 单个资源加载成功回调
     */
    public waitLoad(paths:string[],onComplete:(name:string,res:Asset)=>void){
        for (let it of paths){
            this._waitArray.indexOf(it) === -1 && it !== '' && this._waitArray.push(it);
        }
        if (this._isWait) return ;
        if (this._waitTimer !== null){
            clearInterval(this._waitTimer);
            this._waitTimer = null;
        }
        
        this._waitTimer = setInterval(()=>{
            if (this._isWait) return ;
            const it:string = this._waitArray.shift();
            if (this._waitArray.length < 1){
                clearInterval(this._waitTimer);
                this._waitTimer = null;
            }
            this._isWait = true;
            this.load(it,(assets:Asset)=>{
                onComplete && onComplete(it,assets);
                this._isWait = false;
            });
        },100);
    }

    /**
     * 移除一个子包
     * @param name 子包名
     * @returns 
     */
    public remove(name:string){
        if (name === RES){
            console.warn('resources cannot be removed.')
            return ;
        }
        const bundle:AssetManager.Bundle = this._bundleCache.get(name);
        if (this._isInvalid(bundle)) return ;
        this._bundleCache.delete(name);
        bundle.releaseAll();
        assetManager.removeBundle(bundle);
    }

    /**
     * 加载资源
     * @param path 
     * @param onComplete 
     */
    private _loadAsset(path:string,onComplete:(res:Asset)=>void){
        const subs:string[] = path.split(':');
        const key:string = subs.length < 2 ? RES : subs[1];
        const _bundle:AssetManager.Bundle = this._bundleCache.get(key);
        path = key === RES ? path : `res/${subs[0]}`;
        const _load = (bundle:AssetManager.Bundle)=>{
            bundle.load(path,(err: Error, asset: Asset)=>{
                if (err) 
                    throw new Error(err.message);
                onComplete && onComplete(asset);
            });
        };
        if (this._isInvalid(_bundle)){
            this._loadBundle(key,(bundle:AssetManager.Bundle)=>{
                _load(bundle);
            });
        } else {
            _load(_bundle);
        }
    }
    /**
     * 加载目录中指定类型资源
     * @param dir 
     * abc          加载assets/resources/res/abc 目录
     * res/abc:Bundle 加载子包Hall 路径下assets/.../Bundle/res/abc 目录
     * @param type 目录中加载的资源类型
     * @param onComplete 结束回调
     * @param onProgress 进度回调
     */
    private _loadDir(dir:string,onComplete:CompleteCall,type: typeof Asset,onProgress?:ProgressCall){
        const subs:string[] = dir.split(':');
        const key:string = subs.length < 2 ? RES : subs[1];
        const _bundle:AssetManager.Bundle = this._bundleCache.get(key);
        dir = key === RES ? dir : `res/${subs[0]}`;
        const _load = (bundle:AssetManager.Bundle)=>{
            type ? bundle.loadDir(dir,type,(finish: number, total: number, item: AssetManager.RequestItem) => {
                onProgress && onProgress(finish,total,item.content);
            }, (err: Error, assets: Array<Asset>)=>{
                if (err) throw new Error(err.message);
                onComplete && onComplete(assets);
            }) : bundle.loadDir(dir,(finish: number, total: number, item: AssetManager.RequestItem) => {
                onProgress && onProgress(finish,total,item.content);
            }, (err: Error, assets: Array<Asset>)=>{
                if (err) throw new Error(err.message);
                onComplete && onComplete(assets);
            });
        };
        if (this._isInvalid(_bundle)){
            this._loadBundle(key,(bundle:AssetManager.Bundle)=>{
                _load(bundle);
            });
        } else {
            _load(_bundle);
        }
    }

    /**
     * 加载远程资源
     * @param url 
     * @param cb 
     * @returns 
     */
    private _loadRemote(url:string,onComplete:CompleteCall){
        if (url.indexOf('http://') !== -1 && url.indexOf('https://') !== -1){
            console.error(`AssetsManger.loadRemote(${url}) error.`);
            return ;
        } else {
            assetManager.loadRemote(url, (e:Error, res:Asset) => {
                if (e || !res){
                    console.error(`AssetsManger.loadRemote(${url}) is error.`);
                    return ;
                }
                onComplete && onComplete(res);
            })
        }
    }

    // 对象是否有效
    private _isInvalid<T>(obj:T):boolean{
        if (obj === undefined || obj === null) return true;
        else if (typeof obj === 'number' && isNaN(obj)) return true;
        else if (typeof obj === 'string' && obj.length < 1) return true;
        return  false;
    };

    private _loadBundle(name:string,call:(bundle:AssetManager.Bundle)=>void){
        assetManager.loadBundle(name,(err:Error,bundle:AssetManager.Bundle)=>{
            if (err){
                throw new Error(err.message);
            }
            this._bundleCache.set(name,bundle);
            call && call(bundle);
        });
    }
    /**
     * 
     * @param displayLayout 动画展示节点
     * @param path 动画目录 eg: effects/zhadan:room
     * @param armature 当前的 Armature 名称
     * @param animName 需要播放的动画名 b
     * @param playTimes 播放次数 -1 为使用配置文件中的次数 0 为无限循环播放 > 0 为动画的重复次数
     * @param call 结束回调
     */
    public displayDragonBones(displayLayout:Node, path:string,armature:string, animName: string, playTimes: number = 1, call:()=>void = null){
        console.log(`AssetsMgr.displayDragonBones0(${displayLayout.name},${path},${armature},${animName},${playTimes})`);
        
        this._loadDir(path,(assets:Asset[])=>{
            if (assets.length <= 0){
                call && call();
                return;
            }

            displayLayout.getComponent(dragonBones.ArmatureDisplay) && displayLayout.removeComponent(dragonBones.ArmatureDisplay);
            const ArmatureDisplay:dragonBones.ArmatureDisplay = displayLayout.addComponent(dragonBones.ArmatureDisplay); 

            for (let it of assets){
                if (it instanceof dragonBones.DragonBonesAsset) ArmatureDisplay.dragonAsset  = it;
                if (it instanceof dragonBones.DragonBonesAtlasAsset) ArmatureDisplay.dragonAtlasAsset = it;
            }

            if (ArmatureDisplay.dragonAsset && ArmatureDisplay.dragonAtlasAsset){
                console.log(`AssetsMgr.displayDragonBones1(${displayLayout.name},${path},${armature},${animName},${playTimes})`);
                
                ArmatureDisplay.armatureName = armature;// 一定要设置armatureName
                ArmatureDisplay.playAnimation(animName, playTimes);// 播放动画（同上）
                const playEndCall = (event: Event)=>{
                    ArmatureDisplay.removeEventListener(dragonBones.EventObject.COMPLETE,playEndCall,this);
                    displayLayout.removeComponent(dragonBones.ArmatureDisplay);
                    call && call();
                };
                // 监听
                ArmatureDisplay.addEventListener(dragonBones.EventObject.COMPLETE, playEndCall, this);
            } else {
                console.log(`AssetsMgr.displayDragonBones2(ERROR)`);
                call && call();
            }
        },null);
    }

    public playDragonBones(root:Node,path:string,armature:string,palyName:string,playTimes:number=1,start?:()=>void,end?:()=>void,pos:Vec2=v2(0,0)){
        if (!(root instanceof Node)){
            console.error(`AssetsMgr.playDragonBones(root = null)`);
            return ;
        }
        
        let dragonAsset,atlasAsset;
        this._loadDir(path,(assets:Asset[])=>{
            for (let it of assets){
                if (it instanceof dragonBones.DragonBonesAsset) dragonAsset  = it;
                if (it instanceof dragonBones.DragonBonesAtlasAsset) atlasAsset = it;
            }

            if (this._isInvalid(dragonAsset) || this._isInvalid(atlasAsset)){
                console.error(`AssetsMgr.playDragonBones(DragonBones(${path}) is invalid.)`);
                start && start();
                end && end();
                return ;
            }
            let _node = new Node(path);
            _node.parent = root;
            _node.setPosition(pos.x,pos.y);
            const DB = _node.addComponent<dragonBones.ArmatureDisplay>(dragonBones.ArmatureDisplay);
            DB.dragonAsset = dragonAsset;
            DB.dragonAtlasAsset = atlasAsset;
            DB.armatureName = armature;
            DB.animationName = palyName;
            DB.playTimes = playTimes;
            DB.playAnimation(palyName, playTimes);

            // 监听
            start && DB.addEventListener(dragonBones.EventObject.START, start, this);
            // 监听
            end && DB.addEventListener(dragonBones.EventObject.COMPLETE, ()=>{
                root.removeAllChildren();
                end();
            }, this);
        },null);
    }
}